﻿//
// FixPointCS
//
// Copyright(c) Jere Sanisalo, Petri Kero
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
using System;
using System.Runtime.CompilerServices;
using FixedPoint;

namespace UnityEngine {

    /// <summary>
    /// Vector2 struct with signed 32.32 fixed point components.
    /// </summary>
    public struct Vector2F64 : IEquatable<Vector2F64> {

        // Constants
        public static Vector2F64 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.Zero, Fixed64.Zero); }
        public static Vector2F64 One { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.One, Fixed64.One); }
        public static Vector2F64 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.Zero, Fixed64.Neg1); }
        public static Vector2F64 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.Zero, Fixed64.One); }
        public static Vector2F64 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.Neg1, Fixed64.Zero); }
        public static Vector2F64 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.One, Fixed64.Zero); }
        public static Vector2F64 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.One, Fixed64.Zero); }
        public static Vector2F64 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get => new(Fixed64.Zero, Fixed64.One); }

        // Raw components
        public readonly long RawX => m_RawX;
        private long m_RawX;

        public readonly long RawY => m_RawY;
        private long m_RawY;

        public F64 X {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            readonly get => F64.FromRaw(m_RawX);
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set => m_RawX = value.Raw;
        }

        public F64 Y {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            readonly get => F64.FromRaw(m_RawY);
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set => m_RawY = value.Raw;
        }

        public Vector2F64(F64 x, F64 y) {
            m_RawX = x.Raw;
            m_RawY = y.Raw;
        }

        public Vector2F64(float x, float y) {
            m_RawX = F64.From(x).Raw;
            m_RawY = F64.From(y).Raw;
        }

        // raw ctor only for internal usage
        private Vector2F64(long x, long y) {
            m_RawX = x;
            m_RawY = y;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 FromRaw(long rawX, long rawY) => new(rawX, rawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 From(int x, int y) => new(Fixed64.FromInt(x), Fixed64.FromInt(y));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 From(float x, float y) => new(Fixed64.FromFloat(x), Fixed64.FromFloat(y));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 From(double x, double y) => new(Fixed64.FromDouble(x), Fixed64.FromDouble(y));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 From(F64 x, F64 y) => new(x, y);

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator -(Vector2F64 a) => new(-a.m_RawX, -a.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator +(Vector2F64 a, Vector2F64 b) => new(a.m_RawX + b.m_RawX, a.m_RawY + b.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator -(Vector2F64 a, Vector2F64 b) => new(a.m_RawX - b.m_RawX, a.m_RawY - b.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator *(Vector2F64 a, Vector2F64 b) => new(Fixed64.Mul(a.m_RawX, b.m_RawX), Fixed64.Mul(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator /(Vector2F64 a, Vector2F64 b) => new(Fixed64.DivPrecise(a.m_RawX, b.m_RawX), Fixed64.DivPrecise(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator %(Vector2F64 a, Vector2F64 b) => new(a.m_RawX % b.m_RawX, a.m_RawY % b.m_RawY);

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator +(F64 a, Vector2F64 b) => new(a.Raw + b.m_RawX, a.Raw + b.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator +(Vector2F64 a, F64 b) => new(a.m_RawX + b.Raw, a.m_RawY + b.Raw);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator -(F64 a, Vector2F64 b) => new(a.Raw - b.m_RawX, a.Raw - b.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator -(Vector2F64 a, F64 b) => new(a.m_RawX - b.Raw, a.m_RawY - b.Raw);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator *(F64 a, Vector2F64 b) => new(Fixed64.Mul(a.Raw, b.m_RawX), Fixed64.Mul(a.Raw, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator *(Vector2F64 a, F64 b) => new(Fixed64.Mul(a.m_RawX, b.Raw), Fixed64.Mul(a.m_RawY, b.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator /(F64 a, Vector2F64 b) => new(Fixed64.DivPrecise(a.Raw, b.m_RawX), Fixed64.DivPrecise(a.Raw, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator /(Vector2F64 a, F64 b) => new(Fixed64.DivPrecise(a.m_RawX, b.Raw), Fixed64.DivPrecise(a.m_RawY, b.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator %(F64 a, Vector2F64 b) => new(a.Raw % b.m_RawX, a.Raw % b.m_RawY);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 operator %(Vector2F64 a, F64 b) => new(a.m_RawX % b.Raw, a.m_RawY % b.Raw);

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector2F64 a, Vector2F64 b) => a.m_RawX == b.m_RawX && a.m_RawY == b.m_RawY;
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector2F64 a, Vector2F64 b) => a.m_RawX != b.m_RawX || a.m_RawY != b.m_RawY;

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Div(Vector2F64 a, F64 b) { long oob = Fixed64.Rcp(b.Raw); return new(Fixed64.Mul(a.m_RawX, oob), Fixed64.Mul(a.m_RawY, oob)); }
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 DivFast(Vector2F64 a, F64 b) { long oob = Fixed64.RcpFast(b.Raw); return new(Fixed64.Mul(a.m_RawX, oob), Fixed64.Mul(a.m_RawY, oob)); }
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 DivFastest(Vector2F64 a, F64 b) { long oob = Fixed64.RcpFastest(b.Raw); return new(Fixed64.Mul(a.m_RawX, oob), Fixed64.Mul(a.m_RawY, oob)); }
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Div(Vector2F64 a, Vector2F64 b) => new(Fixed64.Div(a.m_RawX, b.m_RawX), Fixed64.Div(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 DivFast(Vector2F64 a, Vector2F64 b) => new(Fixed64.DivFast(a.m_RawX, b.m_RawX), Fixed64.DivFast(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 DivFastest(Vector2F64 a, Vector2F64 b) => new(Fixed64.DivFastest(a.m_RawX, b.m_RawX), Fixed64.DivFastest(a.m_RawY, b.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 SqrtPrecise(Vector2F64 a) => new(Fixed64.SqrtPrecise(a.m_RawX), Fixed64.SqrtPrecise(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Sqrt(Vector2F64 a) => new(Fixed64.Sqrt(a.m_RawX), Fixed64.Sqrt(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 SqrtFast(Vector2F64 a) => new(Fixed64.SqrtFast(a.m_RawX), Fixed64.SqrtFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 SqrtFastest(Vector2F64 a) => new(Fixed64.SqrtFastest(a.m_RawX), Fixed64.SqrtFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 RSqrt(Vector2F64 a) => new(Fixed64.RSqrt(a.m_RawX), Fixed64.RSqrt(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 RSqrtFast(Vector2F64 a) => new(Fixed64.RSqrtFast(a.m_RawX), Fixed64.RSqrtFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 RSqrtFastest(Vector2F64 a) => new(Fixed64.RSqrtFastest(a.m_RawX), Fixed64.RSqrtFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Rcp(Vector2F64 a) => new(Fixed64.Rcp(a.m_RawX), Fixed64.Rcp(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 RcpFast(Vector2F64 a) => new(Fixed64.RcpFast(a.m_RawX), Fixed64.RcpFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 RcpFastest(Vector2F64 a) => new(Fixed64.RcpFastest(a.m_RawX), Fixed64.RcpFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Exp(Vector2F64 a) => new(Fixed64.Exp(a.m_RawX), Fixed64.Exp(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 ExpFast(Vector2F64 a) => new(Fixed64.ExpFast(a.m_RawX), Fixed64.ExpFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 ExpFastest(Vector2F64 a) => new(Fixed64.ExpFastest(a.m_RawX), Fixed64.ExpFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Exp2(Vector2F64 a) => new(Fixed64.Exp2(a.m_RawX), Fixed64.Exp2(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Exp2Fast(Vector2F64 a) => new(Fixed64.Exp2Fast(a.m_RawX), Fixed64.Exp2Fast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Exp2Fastest(Vector2F64 a) => new(Fixed64.Exp2Fastest(a.m_RawX), Fixed64.Exp2Fastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Log(Vector2F64 a) => new(Fixed64.Log(a.m_RawX), Fixed64.Log(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 LogFast(Vector2F64 a) => new(Fixed64.LogFast(a.m_RawX), Fixed64.LogFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 LogFastest(Vector2F64 a) => new(Fixed64.LogFastest(a.m_RawX), Fixed64.LogFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Log2(Vector2F64 a) => new(Fixed64.Log2(a.m_RawX), Fixed64.Log2(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Log2Fast(Vector2F64 a) => new(Fixed64.Log2Fast(a.m_RawX), Fixed64.Log2Fast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Log2Fastest(Vector2F64 a) => new(Fixed64.Log2Fastest(a.m_RawX), Fixed64.Log2Fastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Sin(Vector2F64 a) => new(Fixed64.Sin(a.m_RawX), Fixed64.Sin(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 SinFast(Vector2F64 a) => new(Fixed64.SinFast(a.m_RawX), Fixed64.SinFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 SinFastest(Vector2F64 a) => new(Fixed64.SinFastest(a.m_RawX), Fixed64.SinFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Cos(Vector2F64 a) => new(Fixed64.Cos(a.m_RawX), Fixed64.Cos(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 CosFast(Vector2F64 a) => new(Fixed64.CosFast(a.m_RawX), Fixed64.CosFast(a.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 CosFastest(Vector2F64 a) => new(Fixed64.CosFastest(a.m_RawX), Fixed64.CosFastest(a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Pow(Vector2F64 a, F64 b) => new(Fixed64.Pow(a.m_RawX, b.Raw), Fixed64.Pow(a.m_RawY, b.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFast(Vector2F64 a, F64 b) => new(Fixed64.PowFast(a.m_RawX, b.Raw), Fixed64.PowFast(a.m_RawY, b.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFastest(Vector2F64 a, F64 b) => new(Fixed64.PowFastest(a.m_RawX, b.Raw), Fixed64.PowFastest(a.m_RawY, b.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Pow(F64 a, Vector2F64 b) => new(Fixed64.Pow(a.Raw, b.m_RawX), Fixed64.Pow(a.Raw, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFast(F64 a, Vector2F64 b) => new(Fixed64.PowFast(a.Raw, b.m_RawX), Fixed64.PowFast(a.Raw, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFastest(F64 a, Vector2F64 b) => new(Fixed64.PowFastest(a.Raw, b.m_RawX), Fixed64.PowFastest(a.Raw, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Pow(Vector2F64 a, Vector2F64 b) => new(Fixed64.Pow(a.m_RawX, b.m_RawX), Fixed64.Pow(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFast(Vector2F64 a, Vector2F64 b) => new(Fixed64.PowFast(a.m_RawX, b.m_RawX), Fixed64.PowFast(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 PowFastest(Vector2F64 a, Vector2F64 b) => new(Fixed64.PowFastest(a.m_RawX, b.m_RawX), Fixed64.PowFastest(a.m_RawY, b.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 Length(Vector2F64 a) => F64.FromRaw(Fixed64.Sqrt(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY)));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 LengthFast(Vector2F64 a) => F64.FromRaw(Fixed64.SqrtFast(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY)));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 LengthFastest(Vector2F64 a) => F64.FromRaw(Fixed64.SqrtFastest(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY)));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 LengthSqr(Vector2F64 a) => F64.FromRaw(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector2F64 Normalize(Vector2F64 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrt(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY))); return ooLen * a; }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector2F64 NormalizeFast(Vector2F64 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFast(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY))); return ooLen * a; }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector2F64 NormalizeFastest(Vector2F64 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFastest(Fixed64.Mul(a.m_RawX, a.m_RawX) + Fixed64.Mul(a.m_RawY, a.m_RawY))); return ooLen * a; }

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 Dot(Vector2F64 a, Vector2F64 b) => F64.FromRaw(Fixed64.Mul(a.m_RawX, b.m_RawX) + Fixed64.Mul(a.m_RawY, b.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 Distance(Vector2F64 a, Vector2F64 b) => Length(a - b);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 DistanceFast(Vector2F64 a, Vector2F64 b) => LengthFast(a - b);
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static F64 DistanceFastest(Vector2F64 a, Vector2F64 b) => LengthFastest(a - b);

        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Min(Vector2F64 a, Vector2F64 b) => new(Fixed64.Min(a.m_RawX, b.m_RawX), Fixed64.Min(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Max(Vector2F64 a, Vector2F64 b) => new(Fixed64.Max(a.m_RawX, b.m_RawX), Fixed64.Max(a.m_RawY, b.m_RawY));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Clamp(Vector2F64 a, F64 min, F64 max) => new(Fixed64.Clamp(a.m_RawX, min.Raw, max.Raw), Fixed64.Clamp(a.m_RawY, min.Raw, max.Raw));
        [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2F64 Clamp(Vector2F64 a, Vector2F64 min, Vector2F64 max) => new(Fixed64.Clamp(a.m_RawX, min.m_RawX, max.m_RawX), Fixed64.Clamp(a.m_RawY, min.m_RawY, max.m_RawY));

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector2F64 Lerp(Vector2F64 a, Vector2F64 b, F64 t) {
            long tb = t.Raw;
            long ta = Fixed64.One - tb;
            return new(
                Fixed64.Mul(a.m_RawX, ta) + Fixed64.Mul(b.m_RawX, tb),
                Fixed64.Mul(a.m_RawY, ta) + Fixed64.Mul(b.m_RawY, tb));
        }

        public readonly bool Equals(Vector2F64 other) => this == other;

        public override readonly bool Equals(object obj) {
            if (obj is not Vector2F64) {
                return false;
            }
            return ((Vector2F64)obj) == this;
        }

        public override readonly string ToString() => "(" + Fixed64.ToString(m_RawX) + ", " + Fixed64.ToString(m_RawY) + ")";

        public override readonly int GetHashCode() => HashCode.Combine(m_RawX, m_RawY);
    }
}
